/**
 * \file Unit.cpp
 * \brief Source file for the \a Unit class.
 * \ingroup Unit
 */

#include"Unit.h" 

#include"Field.h"
#include"Log.h"
#include"Map.h"

#include<math.h>
#include<stdio.h>

/**
 * \struct UnitAndPriorityD
 * \brief Struct for sorting \a Unit 's and their respective priorities as a double.
 * \ingroup Unit
 */

struct UnitAndPriorityD{
	Unit *unit;///<The \a Unit in question.
	double priority;///<Priority of this \a Unit.
	
	UnitAndPriorityD( Unit *unit, double priority ){ this->unit = unit; this->priority = priority; }///<Constructor. \param unit \a Unit \param priority Priority for attacking this \a Unit.
	UnitAndPriorityD() {}///<Constructor
};

Unit::Unit(Map *map, Team team, QString fullName, QString tag, UnitAge age){
	m_map = map;
	m_team = team;
	m_field = 0;
	m_name = fullName;
	m_tag = tag;
	m_health = 10;
	m_attack = 0;
	m_defense = 0;
	m_range = 0;
	m_speed = 0;
	m_age = age;
	m_hasRetaliated = true;
}

Unit::~Unit(){
	m_map = 0;
	m_field = 0;
}

/**
 * \fn int compareFieldPriorityCanAttack(FieldPriority *p1, FieldPriority *p2)
 * \brief Comparison function for fields if in both cases an equal attack is possible.
 * 
 * This function compares two fields to decide which one is most interesting to move towards. Main parameters are:
 * - Avoid retaliation if possible.
 * - Choose the best terrain.
 * - Secure the most interesting possible attack for the next turn.
 * - Make yourself least vulnerable.
 * 
 * The proximity and coherence parameters are unused, for now.
 * \ingroup Unit
 */

int compareFieldPriorityCanAttack(FieldPriority *p1, FieldPriority *p2){
	if(p1->retaliation != p2->retaliation){		//First and foremost, avoid retaliation.
		QString text = "I'm moving here, because here I can attack without suffering retaliation.";
		if(p1->retaliation){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
			return 1;
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
			return -1;
		}
	}else if(p1->terrain != p2->terrain){		//Secondly, choose the best terrain
		QString text = "I'm moving here, because I can attack and the terrain is better.";
		if(p1->terrain > p2->terrain){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->terrain - p1->terrain;
	}else if(p1->expectAttackNextTurn != p2->expectAttackNextTurn){//If next turn, you'd be able to attack a more interesting target
		QString text = "I'm moving here, because I can attack and next turn even attack something more interesting.";
		if(p1->expectAttackNextTurn>p2->expectAttackNextTurn){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->expectAttackNextTurn - p1->expectAttackNextTurn;
	}else if(p1->defense != p2->defense){
		QString text = "I'm moving here to not be too vulnerable.";
		if(p1->defense > p2->defense){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->defense - p1->defense;
	}
	//TODO Proximity and Coherence unused if can attack (although they're shitty parameters anyway, imho.)
	if(p1->reason.isEmpty())
		p1->reason = "bleh";
	if(p2->reason.isEmpty())
		p2->reason = "bleh";
	return 0;
}

/**
 * \fn int compareFieldPriorityNoAttack(FieldPriority *p1, FieldPriority *p2)
 * \brief Comparison function for fields if in both cases no attack is possible.
 * 
 * This function compares two fields to decide which one is most interesting to move towards. Main parameters are:
 * - Choose the \a Field that allows you to attack the most interesting target next turn.
 * - If irrelevant proximity difference, choose the \a Field with the most optimal terrain.
 * - Choose the \a Field where you're least vulnerable.
 * - If irrelevant proximity difference, choose the \a Field closest to the center of your army.
 * - If all else doesn't matter, just move as close as you can.
 * \ingroup Unit
 */
int compareFieldPriorityNoAttack(FieldPriority *p1, FieldPriority *p2){
	if(p1->expectAttackNextTurn != p2->expectAttackNextTurn){//First and foremost, move to a field that allows an attack next turn.
		QString text = "I'm moving here, so that next turn I can attack.";
		if(p1->expectAttackNextTurn > p2->expectAttackNextTurn){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->expectAttackNextTurn - p1->expectAttackNextTurn;
	}else if( abs(p1->proximity-p2->proximity) < 2 && p1->terrain != p2->terrain ){//If no big difference in distance, go for best terrain
		QString text = "I'm moving here, because the terrain is better.";
		if(p1->terrain > p2->terrain){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->terrain - p1->terrain;
	}else if( p1->defense != p2->defense ){
		QString text = "I'm moving here to be less vulnerable.";
		if(p1->defense > p2->defense){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->defense - p1->defense;
	}else if(abs(p1->proximity-p2->proximity) < 2 && p1->coherence != p2->coherence){//TODO: Coherence should best be looked into.
		QString text = "I'm moving here to stay closer to my team.";
		if(p1->coherence > p2->coherence){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->coherence - p1->coherence;
	}else{
		if(p1->reason.isEmpty())
			p1->reason = "blah";
		if(p2->reason.isEmpty())
			p2->reason = "blah";
		return p2->proximity-p1->proximity;//TODO
	}
}

/**
 * \fn int compareFieldPriority(const void *priority1, const void *priority2)
 * \brief Comparison function for fields.
 * 
 * The main AI function. It compares two fields and decides which one is the most interesting \a Field to move to. This function itself only compares if the choice of \a Field has an effect on which \a Unit s can be attacked.
 * \ingroup Unit
 */

int compareFieldPriority(const void *priority1, const void *priority2){
	FieldPriority *p1 = (FieldPriority*) priority1;
	FieldPriority *p2 = (FieldPriority*) priority2;
	
	if(p1->attack != p2->attack){	//Always attack most interesting target first.
		QString text = "I'm moving here, so that I can attack the most interesting target.";
		if(p1->attack > p2->attack){
			if(!p1->reason.contains(text))
				p1->reason.append( " " + text );
		}else{
			if(!p2->reason.contains(text))
				p2->reason.append( " " + text );
		}
		return p2->attack - p1->attack;
	}
	
	if(p1->attack > 0){
		return compareFieldPriorityCanAttack(p1, p2);
	}else{
		return compareFieldPriorityNoAttack(p1, p2);
	}
}

void Unit::selectField(FieldPriority *priorities, int num, int (*sort_func)(const void*, const void*))const{
	for(int i = 1; i < num; i++){
		int ret = sort_func(&priorities[0], &priorities[i]);
		if(ret==0){
			if(priorities[0].field == field())
				ret = -1;
			else if(priorities[i].field == field())
				ret = 0;
		}
		if(ret > 0){
			FieldPriority temp = priorities[0];
			priorities[0] = priorities[i];
			priorities[i] = temp;
		}
	}
}

void Unit::doAIMove(){
	sLog->clear();
	sLog->log("Move for " + name() + " with " + QString().setNum(health()) + " health at " + posToString() + ":");
	if(canRetaliate())
		m_hasRetaliated = false;
	
	QList<UnitAndPriority> units = getAttackPriority();
	
	sLog->log("-> I'd prefer to attack " + units[0].unit->name() + " with " + QString().setNum(units[0].unit->health()) + " health at " + units[0].unit->posToString() );
	
	QList<Field*> fields = m_map->getAccessibleFields(this);
	FieldPriority *fieldPriorities = new FieldPriority[fields.size()];
	
	for(int i = 0; i < fields.size(); i++){
		fieldPriorities[i].field = fields[i];
		fieldPriorities[i].attack = getAttackPriorityForField(fields[i], units);
		fieldPriorities[i].terrain = getFieldPriorityForField(fields[i]);
		fieldPriorities[i].defense = getDefensePriorityForField(fields[i], units);
		fieldPriorities[i].proximity = getProximityPriorityForField(fields[i], units);
		fieldPriorities[i].retaliation = getRetaliateForField(fields[i], units);
		fieldPriorities[i].expectAttackNextTurn = getExpectAttackNextTurnForField(fields[i], units);
		fieldPriorities[i].coherence = getCoherenceForField(fields[i]);
	}
	
	selectField(fieldPriorities, fields.size(), compareFieldPriority);
// 	qsort(fieldPriorities, fields.size(), sizeof(FieldPriority), compareFieldPriority );
	
	for(int i = fields.size()-1; i >= 0; i--){
		printf("Tile: (%d,%d)\n", fieldPriorities[i].field->row(), fieldPriorities[i].field->col());
		printf("\tAttack: %d\n", fieldPriorities[i].attack);
		printf("\tTerrain: %d\n", fieldPriorities[i].terrain);
		printf("\tDefense: %d\n", fieldPriorities[i].defense);
		printf("\tProximity: %d\n", fieldPriorities[i].proximity);
		printf("\tRetaliation: %d\n", fieldPriorities[i].retaliation);
		printf("\tExpectAttackNextTurn: %d\n", fieldPriorities[i].expectAttackNextTurn);
		printf("\tCoherence: %d\n", fieldPriorities[i].coherence);
	}
	printf("\n\n\n");
	
	//This should prioritize standing still, if alternatives are of equal value.
	for(int i = 0; i < fields.size()-1; i++)
		if(compareFieldPriority(&fieldPriorities[i], &fieldPriorities[i+1]))
			break;
		else if(fieldPriorities[i+1].field == field()){
			fieldPriorities[0] = fieldPriorities[i+1];
			break;
		}
	sLog->log("->" + fieldPriorities[0].reason);
	m_map->moveUnit(this, fieldPriorities[0].field);
	attackIfCan(units);
	
	delete [] fieldPriorities;
	fieldPriorities = 0;
}

void Unit::attackIfCan(QList<UnitAndPriority> unitPriority){
	for(int i = 0; i < unitPriority.size(); i++){
		if(canAttack(unitPriority[i].unit)){
			attack(unitPriority[i].unit);
			if(unitPriority[i].unit->canRetaliate(this))
				unitPriority[i].unit->retaliate(this);
			else
				printf("\tCan't retaliate.\n");
			return;
		}
	}
}

bool Unit::canAttack(Unit *other){
	return m_field->distance(other->field()) <= range();
}

int Unit::damageTo(Unit *other)const{
	double damage = 0.0;
	int atk = attack() + attackBonusAgainst(other->type());
	int def = other->defense() + other->defenseBonusAgainst(type());
	damage = atk * 1.5 / def;
	
	//Health modifier. Say attacks are 30% weaker if 1/10 hp
	damage *= 1.0 - (10-health()) / 30;
	
	if(damage < 9.0){//FIXME: There appears to be some randomization in the dmg formula...
		damage += rand()%3-1.0;
	}else
		damage = 9.0;
	return round(damage);
}

//TODO: this should more or less be a proper approximation of the damage formula, or at least sufficient for this purpose.
void Unit::attack(Unit *other){
	int dmg = damageTo(other);
	//TODO Text output could better be shown in GUI imho...
	sLog->log("I attacked " + other->name() + " for " + QString().setNum(dmg) + " health.");
// 	printf("%s attacked %s for %d points.\n", name().toUtf8().data(), other->name().toUtf8().data(), dmg);
	other->damage(dmg);
}

bool Unit::canRetaliate(Unit *other){
	if(m_health <= 0 || !canRetaliate())
		return false;
	return !m_hasRetaliated && m_field->distance(other->field()) <= range();
}

bool Unit::canRetaliate(bool immediately)const{
	if(!immediately)
		return type() != TYPE_LR && type() != TYPE_SR;
	return !m_hasRetaliated && type() != TYPE_LR && type() != TYPE_SR;
}

void Unit::retaliate(Unit *other){
	m_hasRetaliated = true;
	int dmg = damageTo(other);
	sLog->log(name() + " retaliated for " + QString().setNum(dmg) + " damage.");
	other->damage(dmg);
}

void Unit::damage(int dmg){
	m_health -= dmg;
	emit tookDamage();
	if(m_health <= 0)
		die();
}

void Unit::die(){
	sLog->log(name() + " died.");
	m_map->remove(this);
}

void Unit::setField(Field *field){
	if(m_field)
		m_field->setOccupiedBy(0);
	m_field = field;
	if(m_field)
		m_field->setOccupiedBy(this);
}

int compareUnitPriority(const void *unitPriority1, const void *unitPriority2){
	UnitAndPriorityD *p1 = (UnitAndPriorityD*) unitPriority1;
	UnitAndPriorityD *p2 = (UnitAndPriorityD*) unitPriority2;
	return p2->priority - p1->priority;
}

QList<UnitAndPriority> Unit::getAttackPriority(){
	QList<Unit*> enemies = m_map->getEnemyUnits(this);
	UnitAndPriorityD *priorities = new UnitAndPriorityD[enemies.size()];
	
	for(int i = 0; i < enemies.size(); i++)
		priorities[i] = UnitAndPriorityD(enemies[i], getAttackPriority(enemies[i]) );
	
	qsort(priorities, enemies.size(), sizeof(UnitAndPriorityD), compareUnitPriority );
	
	QList<UnitAndPriority> ret;
	int counter = 1;
	for(int i = enemies.size() - 1; i >= 0; i--){
		ret.push_front(UnitAndPriority( priorities[i].unit, counter ));
		if(i > 0 && priorities[i].priority < priorities[i-1].priority){
			counter++;
		}
	}
	delete [] priorities;
	return ret;
}


int Unit::getAttackPriorityForField(Field *field, const QList<UnitAndPriority> &unitPriority)const{
	for(int i = 0; i < unitPriority.size(); i++)
		if( field->distance(unitPriority[i].unit->field()) <= range() )
			return unitPriority[i].priority;
	return 0;
}

int Unit::getDefensePriorityForField(Field *field, const QList<UnitAndPriority> &unitPriority)const{
	int counter = 0;
	for(int i = 0; i < unitPriority.size(); i++)
		if( field->distance(unitPriority[i].unit->field()) <= unitPriority[i].unit->range() + unitPriority[i].unit->speed()/2 )
			counter++;
	return 8 - counter;
}

int Unit::getProximityPriorityForField(Field *field, const QList<UnitAndPriority> &unitPriority)const{
	int d = NUM_ROWS + NUM_COLS;
	for(int i = 0; i < unitPriority.size(); i++){
		int d1 = field->distance(unitPriority[i].unit->field());
		if( d1 < d )
			d = d1;
	}
	return NUM_ROWS + NUM_COLS - d;
}

bool Unit::getRetaliateForField(Field *field, const QList<UnitAndPriority> &unitPriority)const{
	Unit *expectedToAttack = 0;
	int priority = 0;
	for(int i = 0; i < unitPriority.size(); i++){
		if(unitPriority[i].priority > priority && field->distance(unitPriority[i].unit->field()) <= range()){
			expectedToAttack = unitPriority[i].unit;
			priority = unitPriority[i].priority;
		}
	}
	if(!expectedToAttack)
		return false;
	else
		return expectedToAttack->canRetaliate(true) && field->distance(expectedToAttack->field()) <= expectedToAttack->range();
}

int Unit::getExpectAttackNextTurnForField(Field *field, const QList<UnitAndPriority> &unitPriority)const{
	int ret = 0;
	
	for(int i = 0; i < unitPriority.size(); i++){
		if(ret > unitPriority[i].priority)
			continue;
// 		printf("travelDistance: %d\n", m_map->travelDistance(field, unitPriority[i].unit->field()));
		if(m_map->travelDistance( field, unitPriority[i].unit->field() ) - range() * 2 < speed())
			ret = unitPriority[i].priority;
	}
	
	return ret;
}

int Unit::getCoherenceForField(Field *field)const{
	QList<Unit*> friends = m_map->getFriendlyUnits(this);
	double row = 0.0;
	double col = 0.0;
	int count = 0;
	for(int i = 0; i < friends.size(); i++){
		row += friends[i]->field()->row();
		col += friends[i]->field()->col();
		count++;
	}
	if(count==0)
		return 0;
	row /= count;
	col /= count;
	return NUM_COLS + NUM_ROWS - field->distance(m_map->field(round(row), round(col)));
}

QString Unit::posToString()const{
	return "(" + QString().setNum(field()->col()) + "," + QString().setNum(field()->row()) + ")";
}
